
struct Point {
    x: f64,
    y: f64,
}

impl Point {
    fn origin() -> Point {
        Point{ x: 0.0, y: 0.0 }
    }
    fn new(x: f64, y: f64) -> Point {
        Point { x, y }
    }
}

struct Rectangle {
    p1: Point,
    p2: Point,
}
impl Rectangle {
    fn area(&self) -> f64 {
        let Point {x: x1, y: y1 } = self.p1;
        let Point {x: x2, y: y2 } = self.p2;

        ((x1 - x2) * (y1 - y2)).abs()
    }
    fn perimeter(&self) -> f64 {
        let Point {x: x1, y: y1 } = self.p1;
        let Point {x: x2, y: y2 } = self.p2;

        2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
    }
    fn translate(&mut self, x: f64, y: f64) {
        self.p1.x += x;
        self.p2.x += x;

        self.p1.y += y;
        self.p2.y += y;
    }
}

struct Pair(Box<i32>, Box<i32>);
impl Pair {
    fn destroy(self){
        let Pair(first, second) = self;
        println!("Destroying Pair ({}, {})", first, second);
    }
}

fn asso_func(){
    let p = Point::new(1_f64, 2_f64);
    println!("P::x = {}, P::y = {}", p.x, p.y);

    let rectangle = Rectangle {
        p1: Point::origin(),
        p2: Point::new(3.0, 4.0),
    };

    println!("Rectangle perimeter: {}", rectangle.perimeter());
    println!("Rectangel area: {}", rectangle.area());

    let mut square = Rectangle {
        p1: Point::origin(),
        p2: Point::new(1.0, 1.0),
    };

    square.translate(1.0, 1.0);

    let pair = Pair(Box::new(1), Box::new(2));

    pair.destroy();
}



fn fizzbuzz(n : u32) -> (){

    fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
        if rhs == 0 { return false; }

        lhs % rhs == 0
    }
    if is_divisible_by(n , 15) {
        println!("fizzbuzz");
    } else if is_divisible_by(n , 3) {
        println!("fizz");
    } else if is_divisible_by(n, 5) {
        println!("buzz");
    } else {
        println!("{}", n);
    }
}

fn fizzbuzz_to(n: u32) {
    for n in 1..=n {
        fizzbuzz(n);
    }
}


fn closure_easy(){
    let i = 1;

    fn function(i: i32) -> i32 { i + 1 }
    println!("function: {}", function(i));

    let closure_annotated = | i: i32 | -> i32 { i + 1};
    println!("closure_annotated: {}", closure_annotated(i));

    let closure_inferred  = | i      |          i + 1 ;
    println!("closure_inferred: {}", closure_inferred(i));

    let one = || 1;
    println!("closure returning one: {}", one());
}
fn closure_capturing(){
    use std::mem;

    let color = String::from("green");

    let print = || println!("color : {}", color);
    print();

    let _reborrow = &color;
    print();

    let _color_moved = color;
    let mut count = 0;

    let mut inc = || {
        count +=  1;
        println!("count : {}", count);
    };
    inc();

    //let _reborrow = &count; //ERROR, reborrow error; inc colosure mutable borrows count
    inc();

    let _count_reborrowed = &mut count; // closure no longer needs to borrow &mut count

    let movable = Box::new(3);
    let consume = || {
        println!("movable: {:?}", movable);
        mem::drop(movable);
    };

    consume(); //OK

    //consume();//failed

}
fn closure_capturing_move(){
    let haystack = vec![1, 2, 3];
    let contains = move |needle| haystack.contains(needle);

    println!("{}", contains(&1));
    println!("{}", contains(&4));

    //println!(" There are {} elements in vec", haystack.len());
    //Compile error ,  haystack move ownship into contains closure;
}


//Fn: the closure uses the captured value by reference (&T)
//FnMut: the closure uses the captured value by mutable reference (&mut T)
//FnOnce: the closure uses the captured value by value (T) ::-> category: copy or move
fn apply<F>(f: F) where F: FnOnce() {
    f();
}
fn apply_to_3<F>(f: F) -> i32 where F: Fn(i32) -> i32{
    f(3)
}
fn closure_as_input_param(){
    use std::mem;

    let greeting = "hello";

    let mut farewell = "goodbye".to_owned(); //farewell is mut String type

    let diary = ||{
        println!("I said {}.", greeting); //capturing reference value, require 'Fn'
    
        farewell.push_str("!!!"); // capturing mutable reference value, require 'FnMut'

        println!("Then I screamed {}.", farewell);
        println!("Now I can sleep. zzzzzz");
        
        mem::drop(farewell); // captured value with ownship, require 'FnOnce'
    };

    apply(diary);

    let double = |x| 2 * x;
    println!("3 doubled: {}", apply_to_3(double));
}


fn closure_internal(){

    //  closure is unknown type -> generic type paramter <F> needed
    //  T is still ambiguous -> define bounding for closure
    fn apply<F: Fn()>(f: F) {
    //fn apply<F>(f: F) where F: Fn() {
        f();
    }

    let x = 7;
    // compile create unknow type -> struct {x}; impl with one of ( Fn, FnMut, FnOnce) traits
    let print = || println!("{}", x); // Fn(ref) FnMut(mut ref), FnOnce(value)

    apply(print);
}


fn closure_input_functions(){
    fn call_me<F: Fn()>(f: F) {
        f();
    }
    fn function(){
        println!("I'm a function");
    }

    let closure = || println!("I'am a closure");
    call_me(closure);
    call_me(function);
}
fn closure_output_functions(){
    fn create_fn() -> impl Fn() {
        let text = "Fn".to_owned();
        move || println!("This is a : {}", text)
    }
    let fn_plain = create_fn();
    fn_plain();

    fn create_fnmut() -> impl FnMut() {
        let text = "FnMut".to_owned();
        move || println!("This is a : {}", text)
    }
    let mut fn_mut = create_fnmut();
    fn_mut();

    fn create_fnonce() -> impl FnOnce() {
        let text = "FnOnce".to_owned();
        move || println!("This is a : {}", text)
    }
    let fn_once = create_fnonce();
    fn_once();
}

fn example_in_std_iterator_any(){
    let vec1 = vec![1, 2, 3];
    //iter() for vecs yields &i32. Destructure to i32
    println!("2 in vec1: {}", vec1.iter().any(|&x| x == 2));

    let vec2 = vec![4, 5, 6];
    //into_iter() for vecs yields i32, no destructuring required
    println!("2 in vec2: {}", vec2.into_iter().any(|x| x == 2));

    let array1 = [1, 2, 3];
    println!("2 in array1: {}", array1.iter().any(|&x| x == 2));

    let array2 = [4, 5, 6];
    println!("2 in array2: {}", array2.into_iter().any(|x| x == 2));
}

fn closures() {
    closure_easy();
    closure_capturing();
    closure_capturing_move();
    closure_as_input_param();
    closure_internal();
    closure_input_functions();
    closure_output_functions();

    example_in_std_iterator_any();
}











fn print_int(v: &usize){
    println!("print_int with callback {v}");
}
fn print_two(x: &usize, y: &usize){
    println!("print_two with callback {x}:{y}");
}
fn use_fn_traits(){
    {// function ptr : fn

        let f_ptr : fn(&usize) = print_int;
        f_ptr(&10_usize);

        //let no_f_ptr = print_int; // this print_int is ZST (uniquely identifies of function print_int)
    }
    { // Fn(&usize)
        let func: Box<dyn Fn(&usize)> = Box::new(print_int);
        func(&10_usize);
    }

    { // Fn(&usize, &usize)
        let func: Box<dyn Fn(&usize, &usize)> = Box::new(print_two);
        func(&10_usize, &20_usize);
    }

    { // FuMut
        let mut c = 10;
        let fm = |x:& usize, y:& usize| {
            c += x + y;
            println!("{x}:{y}:{c}");
        };
        let mut func: Box<dyn FnMut(&usize, &usize)> = Box::new(fm);
        func(&10_usize, &20_usize);
    }

    { // FuOnce
        let func: Box<dyn FnOnce(&usize, &usize)> = Box::new(print_two);
        func(&10_usize, &20_usize);
    }

    fn change(v: &mut usize){
        *v += 10;
        println!("mut change :{v}");
    }

    {// Fn(&mut usize)
        let func:Box<dyn FnOnce(&mut usize)> = Box::new(change);
        let mut v = 100;
        func(&mut v);
    }

}

pub fn main(){
    fizzbuzz_to(100);
    asso_func(); 
    closures();

    use_fn_traits();
}
